#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "MuseuVirtual.h"
#include "ply_import.h"

#define FOLDER_MUSEUM       "Models/Museum"
#define FOLDER_OBJECTS      "Models/Objects"
#define MUSEUM_FLOOR_NAME   "floor.ply"
#define MUSEUM_CEIL_NAME    "ceil.ply"
#define MUSEUM_WALLS_NAME   "walls.ply"

static char *find_base_dir_models() {
  char dir[ 2040];
  FILE *fi = NULL;
  // looking for model: FOLDER_MUSEUM / MUSEUM_FLOOR_NAME
  for ( int i = 0; i < 3; i++) {
    dir[ 0] = '\0';
    for ( int j = 0; j < i; j++) {
      strcat( dir, "../");
    }
    char buf[ 2048];
    sprintf( buf, "%s%s/%s", dir, FOLDER_MUSEUM, MUSEUM_FLOOR_NAME);
    fi = fopen( buf, "r");
    if ( fi) {
      break;
    }
  }
  char *ret = NULL;
  if ( fi) {
    fclose( fi);
    ret = strdup( dir);
  }
  return ret;
}

Object *read_ply_object( const char *filename) {
  Object *ret_obj = NULL;
  int num_points = 0;
  int num_lines = 0;
  int num_triangles = 0;
  int num_quads = 0;
  float *lst_points = NULL;
  int *lst_lines = NULL;
  int *lst_triangles = NULL;
  int *lst_quads = NULL;
  float *lst_normals = NULL;
  float *lst_scalar = NULL;
  char final_message[ 1024];
  
  int ret = read_ply_file( filename,
                           &num_points, &num_lines, &num_triangles, &num_quads,
                           &lst_points, &lst_lines, &lst_triangles, &lst_quads, &lst_normals, &lst_scalar);
  if ( !ret) {
    const char *err_str = get_ply_error();
    if ( err_str) {
      strcpy( final_message, err_str);
    } else {
      sprintf( final_message, "Error reading file '%s'.", filename);
    }
    ret_obj = NULL;
  }
  
  if ( num_points && ( num_lines || num_triangles || num_quads)) {
    assert( lst_points && ( lst_lines || lst_triangles || lst_quads));
    
    ret_obj = new Object( num_points, num_lines, num_triangles, num_quads,
                          lst_points, lst_lines, lst_triangles, lst_quads,
                          lst_normals, lst_scalar);
    printf( "File '%s' read.\n", filename);
  }
  if ( !ret) {
    printf( "-----> %s\n", final_message);
  }
  return ret_obj;
}

void MuseuVirtual::read_museum() {
  char *dir = find_base_dir_models();
  if ( dir) {
    // const char *lst_parts[] = { MUSEUM_FLOOR_NAME, MUSEUM_CEIL_NAME, MUSEUM_WALLS_NAME, NULL};
    const char *lst_parts[] = { MUSEUM_FLOOR_NAME, MUSEUM_CEIL_NAME, NULL};
    char buf[ 2048];
    for ( int i = 0; lst_parts[ i]; i++) {
      sprintf( buf, "%s%s/%s", dir, FOLDER_MUSEUM, lst_parts[ i]);
      Object *obj = read_ply_object( buf);
      if ( obj) {
        obj->set_name( lst_parts[ i]);
        obj->set_draw_bbox( false);
        _lstMuseum.append( obj);
      }
    }
    if ( _lstMuseum.size()) {
      _bboxMuseum = _lstMuseum[ 0]->get_bbox();
      for ( size_t im = 1; im < _lstMuseum.size(); im++) {
        _bboxMuseum.update( _lstMuseum[ im]->get_bbox());
      }
    }

    // now the walls
    int num_walls = 20;
    for ( int i = 0; i < num_walls; i++) {
      char name[ 20];
      sprintf( name, "wall%02d.ply", i + 1);
      sprintf( buf, "%s%s/%s", dir, FOLDER_MUSEUM, name);
      Object *obj = read_ply_object( buf);
      if ( obj) {
        obj->set_name( name);
        obj->set_draw_bbox( false);
        _lstWalls.append( obj);
      }
    }
    if ( _lstWalls.size()) {
      _bboxMuseum = _lstWalls[ 0]->get_bbox();
      for ( size_t im = 1; im < _lstWalls.size(); im++) {
        _bboxMuseum.update( _lstWalls[ im]->get_bbox());
      }
    }
    free( dir);
  }
}

void MuseuVirtual::read_pedestals() {
  char *dir = find_base_dir_models();
  if ( dir) {
    const char *lst_parts[] = {
      "pedestal11.ply", "pedestal12.ply", "pedestal13.ply", // tres pedestales en la habitacion 1
      "pedestal21.ply", "pedestal22.ply", "pedestal23.ply", "pedestal24.ply", // 4 en la 2
      "pedestal31.ply", "pedestal32.ply", // 2 en la 3
      NULL};
    char buf[ 2048];
    for ( int i = 0; lst_parts[ i]; i++) {
      sprintf( buf, "%s%s/%s", dir, FOLDER_MUSEUM, lst_parts[ i]);
      Object *obj = read_ply_object( buf);
      if ( obj) {
        obj->set_name( lst_parts[ i]);
        obj->set_draw_bbox( false);
        _lstPedestals.append( obj);
      }
    }
    free( dir);
  }
}

void MuseuVirtual::read_objects() {
  char *dir = find_base_dir_models();
  if ( dir) {
#ifdef _DEBUG
    const char *lst_parts[] = {
      "teapot.ply", "teapot.ply", "teapot.ply", 
      "teapot.ply", "teapot.ply", "teapot.ply", "teapot.ply",
      "teapot.ply", "teapot.ply", 
      NULL};
#else
    // release models:
    const char *lst_parts[] = {
      "teapot.ply", "copa.ply", "bunny_standford.ply",
      "armadillo.ply", "hand.ply", "colada.ply", "plataforma.ply",
      "dragon.ply", 
      "happy.ply", 
      NULL};
#endif
    char buf[ 2048];
    for ( int i = 0; lst_parts[ i]; i++) {
      sprintf( buf, "%s%s/%s", dir, FOLDER_OBJECTS, lst_parts[ i]);
      Object *obj = read_ply_object( buf);
      if ( obj) {
        obj->set_name( lst_parts[ i]);
        obj->set_draw_bbox( false);
        _lstObjects.append( obj);
      }
    }
    free( dir);
  }
}

void MuseuVirtual::put_objects_on_pedestals() {
  // scale objects to fit in museum:

  float height = _bboxMuseum.diff().y();
  size_t end = _lstObjects.size();
  if ( end > _lstPedestals.size())
    end = _lstPedestals.size();
  // suppose pedestal grounded
  for ( size_t iobj = 0; iobj < end; iobj++) {
    float ped_height = _lstPedestals[ iobj]->get_bbox().diff().y();
    float height_obj = height - ped_height - 0.5f; // + margin
    Punto min_vitrina( _lstPedestals[ iobj]->get_bbox().min());
    Punto max_vitrina( _lstPedestals[ iobj]->get_bbox().max());
    min_vitrina.y() = _lstPedestals[ iobj]->get_bbox().max().y() + 0.25; // margin
    max_vitrina.y() = min_vitrina.y() + height_obj;
    _lstObjects[ iobj]->fit_into( Box( min_vitrina, max_vitrina));
  }
}
